#include "shell.h"
#include "stdint.h"
#include "file.h"
#include "syscall.h"
#include "stdio.h"
#include "global.h"
#include "assert.h"
#include "string.h"
#include "buildin_cmd.h"

#define cmd_len 128   // 最大支持输入的命令行长度
#define MAX_ARG_NR 16 // 最多可以支持的参数个数

// 存储输入的命令
static char cmd_line[MAX_PATH_LEN] = {0};
char final_path[MAX_PATH_LEN] = {0}; // 用于洗路径时的缓冲

// 记录当前操作的所在目录，每次 cd 都会更新这个路径
char cwd_cache[MAX_PATH_LEN] = {0};

// 输出提示符
void print_prompt(void) {
    printf("[xiaoling@localhost %s]$ ", cwd_cache);
}

// 从键盘缓冲区中最多读入 count 个字节到 buf
static void readline(char* buf, int32_t count) {
    assert(buf != NULL && count > 0);
    char* pos = buf;
    while(read(stdin_no, pos, 1) != -1 && (pos - buf) < count) {
        switch(*pos) {
            // 清空屏幕，保留当前输入
            case 'l' - 'a':
                *pos = 0; // 重置
                clear(); // 清空屏幕
                print_prompt(); // 重新打印提示符
                printf("%s", buf); // 重新将之前输入的命令再次打印
                break;
            // 清空当前输入
            case 'u' - 'a':
                while(buf != pos) {
                    putchar('\b');
                    *(pos--) = 0;
                }
                break;
            case '\n':
            case '\r':
                *pos = 0;
                putchar('\n');
                return;
            case '\b':
                if(buf[0] != '\b') {
                    --pos;
                    putchar('\b');
                }
                break;
            default:
                putchar(*pos);
                pos++;
        }
    }
    printf("readline: can`t find enter_key in the cmd_line, max num of char is 128\n");
}

// 将 cmd_str 字符串以 token 字符分割
static int32_t cmd_parse(char* cmd_str, char** argv, char token) {
    assert(cmd_str != NULL);
    int32_t arg_idx = 0;
    while(arg_idx < MAX_ARG_NR) {
        argv[arg_idx] = NULL;
        arg_idx++;
    }
    char* next = cmd_str;
    int32_t argc = 0;
    while(*next) {
        // 跳过空格
        while(*next == token) next++;
        /* 处理最后一个参数后接空格的清空，如：ls dir  */
        if(*next == 0) break;
        argv[argc] = next;
        // 获取整个有效参数
        while(*next && *next != token) next++;
        // 若未结束
        if(*next) *next++ = 0; // 则以字符串结束符0来表示一个单词的结束
        // 避免越界
        if(argc > MAX_ARG_NR) return -1;
        argc++; // 下一个参数
    }
    return argc;
}

char* argv[MAX_ARG_NR]; // argv 必须为全局变量，为了以后 exec 的程序可以访问到参数
int32_t argc = -1; // 参数个数

// 简单的 shell
void my_shell(void) {
    cwd_cache[0] = '/';
    cwd_cache[1] = 0;
    while(1) {
        print_prompt();
        memset(final_path, 0, MAX_PATH_LEN);
        memset(cmd_line, 0, MAX_PATH_LEN);
        readline(cmd_line, MAX_PATH_LEN);
        if(cmd_line[0] == 0) continue; // 若只输入了一个回车符
        argc = -1;
        argc = cmd_parse(cmd_line, argv, ' ');
        if(argc == -1) {
            printf("num of arguments exceed %d\n", MAX_ARG_NR);
            continue;
        }
        if(!strcmp("ls", argv[0])) buildin_ls(argc, argv);
        else if(!strcmp("cd", argv[0])) {
            if(buildin_cd(argc, argv) != NULL) {
                memset(cwd_cache, 0, MAX_PATH_LEN);
                strcpy(cwd_cache, final_path);
            }
        }
        else if(!strcmp("pwd", argv[0])) buildin_pwd(argc, argv);
        else if(!strcmp("ps", argv[0])) buildin_ps(argc, argv);
        else if(!strcmp("clear", argv[0])) buildin_clear(argc, argv);
        else if(!strcmp("mkdir", argv[0])) buildin_mkdir(argc, argv);
        else if(!strcmp("rmdir", argv[0])) buildin_rmdir(argc, argv);
        else if(!strcmp("rm", argv[0])) buildin_rm(argc, argv);
        else printf("external command.\n");
    }
    panic("my_shell: should not be here");
}
